import type { FileControllerApi } from '../openapi';
import type { ChunkInfo, FileUploadOptions } from './types';

import { FileChunkPacker } from './file-chunk-packer';
import { FileChunkUploader } from './file-chunk-uploader';
import { ProgressStatusManager } from './progress-status';
import { StringFormat } from './utils';

/**
 * 文件上传主类
 */
export class FileUpload {
  private readonly defaultChunkSize: number;
  private readonly fileApi: FileControllerApi;

  constructor(fileApi: FileControllerApi, chunkSize: number = 1024 * 1024) {
    this.fileApi = fileApi;
    this.defaultChunkSize = chunkSize;
  }

  /**
   * 上传文件
   */
  async uploadFile(file: File, options?: FileUploadOptions): Promise<string> {
    const chunkSize = options?.chunkSize || this.defaultChunkSize;
    const onProgress = options?.onProgress;
    const onMd5Progress = options?.onMd5Progress;

    // 创建文件分片打包器
    const packer = new FileChunkPacker(file, chunkSize);

    // 计算文件MD5
    const fileMd5 = await packer.calculateFileMd5(onMd5Progress);

    if (!fileMd5) {
      throw new Error('文件MD5计算失败，MD5值为空！');
    }

    console.log(
      `文件名: ${packer.getFileName}, 文件大小: ${StringFormat.formatSize(packer.getTotalSize)}, 文件 MD5: ${fileMd5}`,
    );

    // 创建文件分片上传器
    const uploader = new FileChunkUploader(
      this.fileApi,
      packer.getFileName,
      fileMd5,
      packer.getTotalSize,
    );

    // 创建进度状态管理器
    const status = new ProgressStatusManager(
      packer.getChunkCount(),
      packer.getTotalSize,
    );

    // 检查是否存在已上传的分片
    let existingChunks: ChunkInfo[] = [];
    const uploadExists = await uploader.checkUploadExists();

    if (uploadExists) {
      console.log(`文件记录已存在，准备断点续传: ${file.name}`);
      existingChunks = await uploader.getAllChunks();
    } else {
      console.log(`无记录，准备上传: ${file.name}`);
      await uploader.start();
    }

    // 开始分片上传
    const chunkIterator = packer.createChunkIterator();

    for await (const chunk of chunkIterator) {
      status.resetWorkingChunk();
      status.workingChunkSize = chunk.chunkData.byteLength;

      try {
        // 检查分片是否已上传
        const existingChunk = existingChunks.find(
          (c) => c.chunkIndex === chunk.chunkIndex,
        );

        if (existingChunk) {
          // 检查MD5是否匹配
          if (existingChunk.chunkMD5 === chunk.chunkMd5) {
            // MD5匹配，跳过上传
            console.log(`分片 [${chunk.chunkIndex}] 已上传，跳过`);
            status.completeCurrentChunk(chunk.chunkData.byteLength);
            onProgress?.(status.getProgressInfo());
            continue;
          } else {
            // MD5不匹配，重新上传
            console.log(
              `分片 [${chunk.chunkIndex}] 已上传，但 MD5 不匹配，重新上传`,
            );
          }
        }

        console.log(
          `开始上传分片: ${chunk.chunkIndex}, 起始位置：${StringFormat.formatSize(chunk.chunkOffset)}，大小：${StringFormat.formatSize(chunk.chunkData.byteLength)}，分片 MD5：${chunk.chunkMd5}`,
        );

        // 上传分片
        const success = await uploader.uploadFileChunk(
          chunk,
          (bytesWritten, contentLength) => {
            status.updateWorkingChunk(bytesWritten, contentLength);
            onProgress?.(status.getProgressInfo());
          },
        );

        if (success) {
          status.completeCurrentChunk(chunk.chunkData.byteLength);
          console.log(`分片 [${chunk.chunkIndex}] 上传成功`);
        } else {
          console.error(`上传失败，跳过分片: ${chunk.chunkIndex}`);
          throw new Error(`分片 ${chunk.chunkIndex} 上传失败`);
        }
      } finally {
        onProgress?.(status.getProgressInfo());
      }
    }

    console.log(`文件上传完成: ${file.name}`);
    return await uploader.complete();
  }
}
